
#include <GL/glu.h>
#include "glDrawFunctionsSkel.h"

vector<Point3> colores;

void glDrawSkeletonBones(Skeleton *skeleton, bool withjoints) {
   
   if (skeleton==0) return;
   
   glPushAttrib(GL_COLOR_BUFFER_BIT | GL_ENABLE_BIT | GL_LINE_BIT);
   glDisable(GL_LIGHTING);
   glDisable(GL_DEPTH_TEST);
   glEnable(GL_LINE_SMOOTH);
   glEnable(GL_BLEND);
   glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
   glLineWidth(8.0);
   for (int i=0; i<skeleton->nBones(); ++i) {
      Segment3 b = skeleton->getBoneSegment(i);
      const Point3 &source = b.source();
      const Point3 &target = b.target();
      glBegin(GL_LINES);
         glVertex3d(source.x(), source.y(), source.z());
         glVertex3d(target.x(), target.y(), target.z());
      glEnd();
   }
   glPopAttrib();
   
   if (withjoints) glDrawSkeletonJoints(skeleton);
}

void glDrawSkeletonJoints(Skeleton *skel) {
   
   glPushAttrib(GL_COLOR_BUFFER_BIT | GL_ENABLE_BIT | GL_POINT_BIT);
   glDisable(GL_LIGHTING);
   glDisable(GL_DEPTH_TEST);
   glEnable(GL_POINT_SMOOTH);
   glEnable(GL_BLEND);
   glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
   glPointSize(14.0);
   glColor3d(1.0, 0.5, 0.0);
   for (int i=0; i<skel->nBones(); ++i) {
      string name = skel->boneName(i);
      const Point3 &p = skel->point(name);
      glPushName(i);
      glBegin(GL_POINTS);
      glVertex3d(p[0], p[1], p[2]);
      glEnd();
      glPopName();
   }
   
   {  // render the root
      const Point3 &p = skel->getRootPosition();
      glPushName(skel->nBones());
      glPointSize(17.0);
      glColor3d(0.7, 0.0, 0.0);
      glBegin(GL_POINTS);
      glVertex3d(p[0], p[1], p[2]);
      glEnd();
      glPopName();
   }
   glPopAttrib();
   
}

void drawSkeletonJoints(GSkeleton *skel) {
   
   if (skel==0) return;
   for (unsigned int i=0; i<skel->nJoints(); ++i) {
      const Point3 &p = skel->getJointPos(i);
      glPushName(i);
      glBegin(GL_POINTS);
      glVertex3d(p[0], p[1], p[2]);
      glEnd();
      glPopName();
   }
}

void drawSkeleton(GSkeleton *skel) {
   
   if (skel==0) return;
   
   glPushAttrib(GL_COLOR_BUFFER_BIT | GL_ENABLE_BIT | GL_POINT_BIT | GL_LINE_BIT);
   glDisable(GL_LIGHTING);
   glDisable(GL_DEPTH_TEST);
   glEnable(GL_POINT_SMOOTH);
   glEnable(GL_LINE_SMOOTH);
   glEnable(GL_BLEND);
   glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
   glLineWidth(6.0);
   glPointSize(14.0);
   glColor3d(0.0, 0.0, 0.0);
   
   //~ glLineWidth(8.0);
   //~ glColor4f(0.5,0.25,0.0,0.9);
   for (int i=0; i<skel->nBones(); ++i) {
      const Segment3 &s = skel->getBoneSegment(i);
      glBegin(GL_LINES);
      glVertex3d(s[0][0], s[0][1], s[0][2]);
      glVertex3d(s[1][0], s[1][1], s[1][2]);
      glEnd();
   }
   //~ glPointSize(16.0);
   //~ glColor4f(0.0,0.0,0.0,0.9);
   for (uint i=0; i<skel->nJoints(); ++i) {
      const Point3 &p = skel->getJointPos(i);
      glPushName(i);
      glBegin(GL_POINTS);
      glVertex3d(p[0], p[1], p[2]);
      glEnd();
      glPopName();
   }
   
   
   //~ glLineWidth(4.0);
   //~ glColor3d(0.0, 0.3, 0.0);
   //~ for (int i=0; i<skel->nBones(); ++i) {
      //~ const Vector3 &ab = skel->atbones[i];
      //~ const Point3 &p = skel->getJointPos(skel->bones[i].first);
      //~ Point3 q = p + ab*5.0;
      //~ glBegin(GL_LINES);
      //~ glVertex3d(p[0], p[1], p[2]);
      //~ glVertex3d(q[0], q[1], q[2]);
      //~ glEnd();
   //~ }
   glPopAttrib();
}

/*
void drawRegions(Mesh *model) {
   
   if (model==0) return ;
   
   float r[9]={0.1,0.2,0.3,0.4,0.5,0.6,0.7,0.8,0.9};
   float g[9]={0.1,0.9,0.2,0.8,0.3,0.7,0.4,0.6,0.5};
   float b[9]={0.9,0.1,0.7,0.6,0.5,0.4,0.3,0.2,0.1};
   
   glDisable(GL_LIGHTING);
   glPointSize(5.0);
   glBegin(GL_POINTS);
   Vertex_iterator vi = model->vertices_begin();
   while (vi != model->vertices_end()) {
      const int &source = vi->source();
      if (source == -1) glColor3f(1, 0, 0);
      else              glColor3f(r[source%9], g[source%9], b[source%9]);
      const Point3 &pv = vi->point();
      glVertex3f(pv[0], pv[1], pv[2]);
      ++vi;
   }
   glEnd();
   glPointSize(1.0);
   glEnable(GL_LIGHTING);
   
}

void drawIsoRings(Mesh *model) {
   
   if (model==0) return ;
   
   for (Facet_iterator fi = model->facets_begin(); fi != model->facets_end(); ++fi) {
      Halfedge_handle hh = fi->halfedge(), hhbegin = hh;
      glBegin(GL_POLYGON);
      do {
         int idx = hh->vertex()->ringId()%colores.size();
         GLfloat color[4] = {colores[idx][0], colores[idx][1], colores[idx][2], 1.0f};
         glMaterialfv(GL_FRONT, GL_DIFFUSE, color);
         const Point3 &p = hh->vertex()->point();
         const Vector3 &n = hh->vertex()->normal();
         glColor4fv(color);
         glNormal3f(n[0], n[1], n[2]);
         glVertex3f(p[0], p[1], p[2]);
         hh = hh->next(); //
      } while(hh != hhbegin);
      glEnd();
   }
}
*/

void glDrawBranches(SkeletonsMatcher *matcher) {
   
   if (matcher == 0) return;
   
   glPushAttrib(GL_COLOR_BUFFER_BIT | GL_ENABLE_BIT | GL_LINE_BIT);
   glDisable(GL_LIGHTING);
   glEnable(GL_LINE_SMOOTH);
   glDisable(GL_DEPTH_TEST); 
   glEnable(GL_BLEND);
   glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
   glLineWidth(8.0);
   
   static vector<Point3> branchColor(9);
   branchColor[0] = Point3(0.6, 0, 0);
   branchColor[1] = Point3(0, 0.5, 0);
   branchColor[2] = Point3(0, 0, 0.7);
   branchColor[3] = Point3(0.7, 0, 0.7);
   branchColor[4] = Point3(0, 0.4, 0.4);
   branchColor[5] = Point3(0.3, 0.3, 0);
   branchColor[6] = Point3(0.0, 0.3, 0.7);
   branchColor[7] = Point3(0.7, 0.3, 0.0);
   branchColor[8] = Point3(0.7, 0.3, 0.7);
   
   //~ {
   //~ const vector<vector<int> > &branches = matcher->gbranches;
   //~ for (uint i=0; i<branches.size(); ++i) {
      //~ int idx = i%branchColor.size();
      //~ GLfloat color[4] = {branchColor[idx][0], branchColor[idx][1], branchColor[idx][2], 1.0f};
      //~ glColor4fv(color);
      //~ glBegin(GL_LINE_STRIP);
      //~ const vector<int> &branch = branches[i];
      //~ for (uint j=0; j<branch.size(); ++j) {
         //~ const Point3 &p = matcher->gskel->getJointPos(branch[j]);
         //~ glVertex3f(p[0], p[1], p[2]);
      //~ }
      //~ glEnd();
   //~ }
   //~ }
   
   //~ glPointSize(10.0);
   //~ {
   //~ const vector<vector<int> > &branches = matcher->gbranches;
   //~ for (uint i=0; i<branches.size(); ++i) {
      //~ int idx = i%colores.size();
      //~ GLfloat color[4] = {colores[idx][0], colores[idx][1], colores[idx][2], 1.0f};
      //~ glColor4fv(color);
      //~ glBegin(GL_POINTS);
      //~ const vector<int> &branch = branches[i];
      //~ for (uint j=0; j<branch.size(); ++j) {
         //~ const Point3 &p = matcher->gskel->getJointPos(branch[j]);
         //~ glVertex3f(p[0], p[1], p[2]);
      //~ }
      //~ glEnd();
   //~ }
   //~ }
   //~ glPointSize(1.0);
   
   
   //~ glLineWidth(10.0);
   //~ {
   //~ const vector<vector<int> > &branches = matcher->gbranches;
   //~ for (int i=0; i<branches.size(); ++i) {
      //~ int idx = i%colores.size();
      //~ GLfloat color[4] = {colores[idx][0], colores[idx][1], colores[idx][2], 1.0f};
      //~ glColor4fv(color);
      //~ glBegin(GL_LINE_STRIP);
      //~ const vector<int> &branch = branches[i];
      //~ for (int j=0; j<branch.size(); ++j) {
         //~ const Point3 &p = matcher->joints[branch[j]];
         //~ glVertex3f(p[0], p[1], p[2]);
      //~ }
      //~ glEnd();
   //~ }
   //~ }
   
   /*
   glLineWidth(6.0);
   {
   const vector<vector<int> > &branches = matcher->ubranches;
   for (uint i=0; i<branches.size(); ++i) {
      int idx = i%branchColor.size();
      GLfloat color[4] = {branchColor[idx][0], branchColor[idx][1], branchColor[idx][2], 1.0f};
      glColor4fv(color);
      glBegin(GL_LINE_STRIP);
      const vector<int> &branch = branches[i];
      for (uint j=0; j<branch.size(); ++j) {
         const Point3 &p = matcher->uskel->getJointPos(branch[j]);
         glVertex3f(p[0], p[1], p[2]);
      }
      glEnd();
   }
   }
   */
   
   glPopAttrib();
}

///
/// below drawing function for rigging/skinning
///
void glDrawRiggingSkinning(Mesh *model, GSkeleton *skeleton, int mode_mask) {
   
   if (mode_mask & RIGGING) 
      glDrawRigging(model, false);
   if (mode_mask & RIGGINGONBONES)
      glDrawRigging(model, true);
   else if (mode_mask & SKINNING) 
      glDrawSkinning(model);
   else if (mode_mask & RIGGINGEDGES) 
      glDrawRiggingEdges(model, skeleton, false);
   else if (mode_mask & RIGGINGEDGESONBONES)
      glDrawRiggingEdges(model, skeleton, true);
}

void glDrawRigging(Mesh *model, bool usingbones) {
   
   if (model == 0) return;
   for (Facet_iterator fi = model->facets_begin(); fi != model->facets_end(); ++fi) {
      Halfedge_handle hh = fi->halfedge(), hhbegin = hh;
      glBegin(GL_POLYGON);
      do {
         int idx=-1;
         if (usingbones and hh->vertex()->closestBone >=0) 
            idx = hh->vertex()->closestBone%colores.size();
         else if (not usingbones and hh->vertex()->closestJoint >=0) 
            idx = hh->vertex()->closestJoint%colores.size();
         if (idx != -1) {
            GLfloat color[4] = {colores[idx][0], colores[idx][1], colores[idx][2], 1.0f};
            glMaterialfv(GL_FRONT, GL_DIFFUSE, color);
            const Point3 &p = hh->vertex()->point();
            const Vector3 &n = hh->vertex()->normal();
            glColor4fv(color);
            glNormal3f(n[0], n[1], n[2]);
            glVertex3f(p[0], p[1], p[2]);
         }
         hh = hh->next(); //
      } while(hh != hhbegin);
      glEnd();
   }
}

void glDrawSkinning(Mesh *model) {
   
   if (model == 0) return;
   GLfloat color[4];
   for (Facet_iterator fi = model->facets_begin(); fi != model->facets_end(); ++fi) {
      Halfedge_handle hh = fi->halfedge(), hhbegin = hh;
      glBegin(GL_POLYGON);
      do {
         unsigned int nminusone = std::count(hh->vertex()->weights.begin(), 
                                       hh->vertex()->weights.end(), 1.0);
         if (nminusone != 1) {
            vector<Number> ltmp(hh->vertex()->weights);
            std::sort(ltmp.begin(), ltmp.end());
            Number large = ltmp.back();
            if (large > 0.9) {
               color[0] = 0.0;
               color[1] = 0.0;
               color[2] = 0.0;
               color[3] = 1.0f;
            }
            else {
               color[0] = large;
               color[1] = large;
               color[2] = large;
               color[3] = 1.0f;
            }
         }
         else {
            color[0] = 0.0;
            color[1] = 0.0;
            color[2] = 0.0;
            color[3] = 1.0f;
         }
         glMaterialfv(GL_FRONT, GL_DIFFUSE, color);
         const Point3 &p = hh->vertex()->point();
         const Vector3 &n = hh->vertex()->normal();
         glColor4fv(color);
         glNormal3f(n[0], n[1], n[2]);
         glVertex3f(p[0], p[1], p[2]);
         hh = hh->next();
      } while(hh != hhbegin);
      glEnd();
   }
}


void glDrawRiggingEdges(Mesh *model, GSkeleton *skeleton, bool usingbones) {
   
   if (model == 0) return;
   if (skeleton == 0) return;
   
   glPushAttrib(GL_COLOR_BUFFER_BIT | GL_POINT_BIT | GL_ENABLE_BIT | GL_POLYGON_BIT);
   glDisable(GL_LIGHTING);
   glEnable(GL_POINT_SMOOTH);
   glEnable(GL_BLEND);
   glEnable(GL_CULL_FACE);
   
   glColor4f(1.0, 0.5, 0.0, 1.0);
   glLineWidth(0.5);
   glBegin(GL_LINES);
   Vertex_iterator vi = model->vertices_begin();
   while (vi != model->vertices_end()) {
      const Point3 &pv = vi->point();
      if (usingbones) {
         const int &j = vi->closestBone;
         if (j==-1) {
            //~ glVertex3f(pv[0], pv[1], pv[2]);
            //~ glVertex3f(0, 0, 0);
         }
         else {
            const Segment3 &b = skeleton->getBoneSegment(j);
            const Point3 &p = b.source();
            const Point3 &q = b.target();
            glVertex3f(pv[0], pv[1], pv[2]);
            glVertex3f((p[0]+q[0])*0.5, (p[1]+q[1])*0.5, (p[2]+q[2])*0.5);
         }
      }
      else {
         const int &j = vi->closestJoint;
         if (j == -1) { ++vi; continue; }
         //~ assert(j != -1 and "rigging group -1");
         //~ assert(j < skeleton->nJoints() and " j > nJoints ");
         if (j >= skeleton->nJoints()) cout<<"j :::: "<<j<<endl;
         const Point3 &p = skeleton->getJointPos(j);
         glVertex3f(pv[0], pv[1], pv[2]);
         glVertex3f(p[0], p[1], p[2]);
      }
      ++vi;
   }
   glEnd();
   glPopAttrib();
   
   //~ glColor4f(0.0, 1.0, 0.0, 1.0);
   //~ vi = model->vertices_begin();
   //~ Vertex_iterator vini = model->vertices_begin();
   //~ while (vi != model->vertices_end()) {
      //~ const int &i = vi->_vertex_parent;
      //~ if (i!=-1) cout<<" i: "<<i<<endl;
      //~ if (i != -1) {
         //~ const Point3 &p = vi->point();
         //~ const Point3 &q = vini[i].point();
         //~ glBegin(GL_POINTS);
         //~ glVertex3f(p[0], p[1], p[2]);
         //~ glVertex3f(q[0], q[1], q[2]);
         //~ glEnd();
      //~ }
      //~ ++vi;
   //~ }
}

///

///

void generateColors() {
   
   colores.reserve(300);
   list<Point3> cores;
   float step=0.2; bool flag = true;
   for (float r=0.0; r<=1.0; r+=step) 
      for (float g=1.0; g>=0.0; g-=step) 
         for (float b=0.0; b<=1.0; b+=step) {
            //~ if (flag) {
               //~ cores.push_back(Point3(r,g,b));
               //~ cores.push_back(Point3(b,r,g));
               //~ cores.push_back(Point3(g,b,r));
            //~ }
            //~ else {
               //~ cores.push_front(Point3(r,g,b));
               //~ cores.push_front(Point3(b,r,g));
               //~ cores.push_front(Point3(g,b,r));
            //~ }
            
            if (flag) cores.push_back(Point3(r,g,b));
            else      cores.push_front(Point3(b,r,g));
            flag = not flag;
         }
   
   colores.push_back(Point3(0.0,0.5,1.0));
   
   colores.push_back(Point3(0.75,0.75,0.0));
   colores.push_back(Point3(0.75,0.0,0.75));
   colores.push_back(Point3(0.5,0.0,0.0));
   colores.push_back(Point3(0.0,1.0,0.5));
   colores.push_back(Point3(0.0,0.0,0.25));
   
   colores.push_back(Point3(0.0,0.3,0.0));
   colores.push_back(Point3(0.7,0.2,0.2));
   colores.push_back(Point3(0.1,0.3,0.3));
   colores.push_back(Point3(0.0,0.2,0.4));
   colores.push_back(Point3(0.5,0.0,1.0));
   
   colores.push_back(Point3(0.4,0.2,0.3));
   colores.push_back(Point3(0.7,0.0,0.0));
   colores.push_back(Point3(0.5,0.5,0.2));
   colores.push_back(Point3(0.25,0.5,0.0));
   colores.push_back(Point3(1.0,0.0,0.5));
   
   colores.push_back(Point3(0.25,0.0,0.0));
   colores.push_back(Point3(1.0,0.5,0.0));
   colores.push_back(Point3(1.0,0.0,0.0));
   colores.push_back(Point3(0.0,1.0,0.0));
   colores.push_back(Point3(0.5,0.0,0.0));
   
   colores.push_back(Point3(0.3,0.3,0.1));
   colores.push_back(Point3(0.6,0.6,0.4));
   
   colores.insert(colores.end(), cores.begin(), cores.end());
}

